﻿# -*- coding: utf-8 -*-​
import numpy as np
np.set_printoptions(precision=2)
np.set_printoptions(suppress=True)
from numpy import pi,sqrt
import os 
import tqdm
import math  

from coil_calculator import calculate_all
import pandas as pd  

import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=["SimHei"] #设置 中文字体
plt.rcParams["axes.unicode_minus"]=False #该语句解决图像中的“-”负号的乱码问题
from matplotlib.animation import FuncAnimation  
import matplotlib
from matplotlib.lines import Line2D  
matplotlib.use('TKAgg')

from scipy.interpolate import interp1d  

from itertools import product  


import tkinter as tk  
from tkinter import ttk  
from tkinter import scrolledtext
from functools import partial

import copy  
from multiprocessing import Pool  

import multiprocessing
import chardet
from zlsx import 根据原有数据生成阵列

__version__ = '5.5.8'  # 设置版本号


#获取路径
script_directory = os.path.dirname(os.path.abspath(__file__))   
basename = os.path.basename(script_directory)  
# 判断末级名称是否是_internal  
if basename == "_internal":  #如果运行打包的程序文件时，需要修正路径
    script_directory= os.path.dirname(script_directory) 



def get_file_encoding(file_path): #判断文件编码
    with open(file_path, 'rb') as f:
        result = chardet.detect(f.read())
    return result['encoding']


class CoilMode():
    def __init__(self,modeidata=0,lin_preset=0,only_id='',Mcaulate_select = '不计算互感',control_mode= '位移控制'  ) -> None:


        self.input_Timing = []


        if lin_preset != 0:

            self.lin_preset=lin_preset

            if only_id != '':
                self.lin_preset['only_id'] =   only_id

            self.lin_preset_To_preset()

        else:
            global modecsv

            modecsv_encoding = get_file_encoding(modecsv)
            print(f"文件编码: {modecsv_encoding}")

            self.Modefile=pd.read_csv(modecsv, index_col=False, encoding= modecsv_encoding)

            #self.Modefile=pd.read_csv(modecsv, index_col=False, encoding='GB2312')


            if(modeidata ==0):
                self.mode=eval(self.Modefile.loc[0,'行选择'])
            else:
                self.mode=modeidata
            
            print("行选择:"+str(self.mode))

            self.modei=int(self.mode[1])-2
            self.modeend=int(self.mode[2])-2
        
            coil_layout=[]
            for i in range(self.modei,self.modeend+1): #提取线圈参数
                coil_layout.append({'模式选择':self.load('模式选择',i), '漆包线材料(CuorAl)':self.load('漆包线材料(CuorAl)',i),'漆包线线径(mm)':self.load('漆包线线径(mm)',i), '漆层厚度(um)':self.load('漆层厚度(um)',i),'线圈长度(mm)':self.load('线圈长度(mm)',i),'匝径start':self.load('匝径start',i),'匝数幂衰减系数':self.load('匝数幂衰减系数',i),'线圈数量':self.load('线圈数量',i),'最大脉宽(pw)':self.load('最大脉宽(pw)',i),'脉宽幂递增系数':self.load('脉宽幂递增系数',i),         '关断策略偏移(mm)':self.load('关断策略偏移(mm)',i)    }     )
            

            if self.load('拓扑')  =='空缺' or self.load('拓扑')  =='半桥':
                topology = '半桥'
                分路数量 = 0
                C2 =0

            elif self.load('拓扑')  =='boost' : 
                topology = 'boost'
                分路数量 =int(self.load('分路数量'))
                C2 =0

                coil_layout=[]
                for i in range(self.modei,self.modeend+1): #提取线圈参数
                    coil_layout.append({'模式选择':self.load('模式选择',i), '漆包线材料(CuorAl)':self.load('漆包线材料(CuorAl)',i),'漆包线线径(mm)':self.load('漆包线线径(mm)',i), '漆层厚度(um)':self.load('漆层厚度(um)',i),'线圈长度(mm)':self.load('线圈长度(mm)',i),'匝径start':self.load('匝径start',i),'匝数幂衰减系数':self.load('匝数幂衰减系数',i),'线圈数量':self.load('线圈数量',i),'最大脉宽(pw)':self.load('最大脉宽(pw)',i),'脉宽幂递增系数':self.load('脉宽幂递增系数',i),         '关断策略偏移(mm)':self.load('关断策略偏移(mm)',i) ,         '电容值(uf)':self.load('电容值(uf)',i)  ,'外部阻抗(esr)':self.load('外部阻抗(esr)',i) }     )
                


            elif self.load('拓扑')  =='全桥谐振' : 
                topology = '全桥谐振'
                分路数量 = int(self.load('分路数量'))
                C2 =  self.load('谐振电容值')  

            elif self.load('拓扑')  =='补能buck谐振' : 
                topology = '补能buck谐振'
                分路数量 = int(self.load('分路数量'))
                C2 =  self.load('谐振电容值')  

            print(f'拓扑:{topology}')
            Secondary_parameters = eval(self.Modefile.loc[0,'次要参数'])
              
            self.lin_preset = {

                'only_id': f'{topology} {control_mode} {Mcaulate_select} 唯一id： {self.mode[0]}', 
                'topology' : topology,
                'Mcaulate_select':Mcaulate_select,

                'Secondary_parameters':Secondary_parameters,
                
                'coil_layout':coil_layout,        #线圈参数
                'L_coil1':self.load('隔板长度(mm)'),     #隔板 mm
                'Inner_diameter':self.load('支架直径(mm)'),   #或者可以称之为 线圈内直径 mm
                
                "R_pow" : self.load('外部阻抗(esr)'),     # 电源电阻
                "U0": self.load('电容耐压值(V)'),             # 初始电压  
                "C"  : self.load('电容值(uf)'),       # 电容

                "slug_R" : self.load('子弹直径(mm)'),       #子弹直径
                "slug_length" : self.load('子弹长度(mm)'),       #子弹长度
                "m" :self.load('子弹质量(g)'),     #子弹质量

                "v0": self.load('初始速度(mps)'),             # 初始速度  
                
                "saturationB_T":self.load('饱和磁导(T)'),  #饱和磁导
                "阵列规模输入" : self.load('阵列规模输入'),   # 阵列规模输入

                "分路数量" : 分路数量,
                "C2" : C2,
                "PARAM_BOUNDS": [],

            }
            

            #################################################参数化扫描
            

                #参数化扫描 线圈参数
            for i,layout in zip(range(len(self.lin_preset["coil_layout"])) ,self.lin_preset["coil_layout"])  :
                for key, value in layout.items(): 

                    if isinstance(value, str) :
                        if value =='空缺':
                            layout[key] = value
                        elif  isinstance(eval(value), list):
                            evalvalue=eval(value)
                            if key == '线圈数量' :
                                self.lin_preset['PARAM_BOUNDS'].append( (f'{[i,key]}', 'int','list' ,evalvalue)  )
                            else:
                                self.lin_preset['PARAM_BOUNDS'].append( (f'{[i,key]}', 'float','list',evalvalue)  )

                        elif isinstance(eval(value), tuple):
                            evalvalue=list(eval(value))
                            self.lin_preset['PARAM_BOUNDS'].append( (f'{[i,key]}', 'float','tuple',evalvalue)  )


                        elif isinstance(eval(value), dict):   #字典类型 就保留 稍后处理
                            layout[key] = value
                            
                        else: #其他为float类型即可
                            layout[key] = float(value)
            
                            
            #参数化扫描 非线圈参数
            for key, value in self.lin_preset.items(): 
                if(key != "coil_layout" and key != "阵列规模输入" and key != "only_id" and key != "Mcaulate_select"  and key != "topology" and key != "PARAM_BOUNDS") :
                    if isinstance(value, str) :
                        if isinstance(eval(value), list):
                            evalvalue=eval(value)
                            self.lin_preset['PARAM_BOUNDS'].append( (f'{[-1,key]}', 'float','list',evalvalue)  )

                        elif isinstance(eval(value), tuple):
                            evalvalue=list(eval(value))
                            self.lin_preset['PARAM_BOUNDS'].append( (f'{[-1,key]}', 'float','tuple',evalvalue)  )

                        elif isinstance(eval(value), dict):   #字典类型 就保留稍后处理
                            layout[key] = value

                        else:
                            self.lin_preset[key] = float(value)



    def lin_preset_To_preset(self):

        #print(self.lin_preset) 
        #print('\n') 
        #####coil_layout 朝上获取最近的有效数值
        for i in range(len(self.lin_preset['coil_layout'])):
            for key, value in self.lin_preset['coil_layout'][i].items():
                #self.lin_preset['coil_layout'][i][key] = float(value)
                #if math.isnan(self.lin_preset['coil_layout'][i][key] )   :
                #  
                if value == "空缺" : 

                    #print(f"i:{i},key:{key},value:{value},{self.lin_preset['coil_layout'][i-1]}")
                    self.lin_preset['coil_layout'][i][key]= self.lin_preset['coil_layout'][i-1][key]
        

        #一些数量项目要取整
        for coil_l in self.lin_preset['coil_layout'] :
           coil_l['线圈数量'] = int(coil_l['线圈数量'])
           coil_l['漆包线材料(CuorAl)'] = int(coil_l['漆包线材料(CuorAl)'])
           coil_l['模式选择'] = int(coil_l['模式选择'])


        coil_number_calculate0 =int(sum(    coil_l['线圈数量'] if coil_l['线圈数量'] > 0 else 0    for coil_l in self.lin_preset['coil_layout']    ))
        #print(f'{coil_number_calculate0}')
        coil_number_calculate =0
        for coil_l in self.lin_preset['coil_layout'] :
            if coil_l['线圈数量'] <0 :
                paoguan_length_mm = abs(coil_l['线圈数量'])

                coil_l['线圈数量'] =   int( paoguan_length_mm/(coil_l['线圈长度(mm)']+ self.lin_preset['L_coil1'] ) )    - coil_number_calculate0
                #print(f'{paoguan_length_mm},{coil_l['线圈长度(mm)']} ,{coil_l['线圈数量']}' )


            coil_number_calculate += int(coil_l['线圈数量'])





        
        if self.lin_preset['v0'] == 0: #速度填0时调整为0.01mps
            mypreset_v0 = 0.01
        else :
            mypreset_v0 = float(self.lin_preset['v0'])


        self.useSteel_ball = 0
        if self.lin_preset['slug_length'] == -1:  #钢珠
            self.useSteel_ball = 1
            mypreset_slug_length = self.lin_preset['slug_R']*1e-3
        else :
            mypreset_slug_length = self.lin_preset['slug_length']*1e-3


        if self.lin_preset['m'] == 0:  #启用自动计算质量功能
            if  self.useSteel_ball == 0: #自动计算定位销重量
                mypreset_m = self.calculate_cylinder_mass( float(self.lin_preset['slug_R']) , float(self.lin_preset['slug_length']),self.lin_preset['Secondary_parameters']['子弹密度(g/cm3)'] ) 
            else:#自动计算钢珠重量
                mypreset_m = self.calculate_steel_ball_mass( float(self.lin_preset['slug_R']) ,self.lin_preset['Secondary_parameters']['子弹密度(g/cm3)']       )
        else :
            mypreset_m = float(self.lin_preset['m']*1e-3)



        #计算C
        mylinpreset_C =self.lin_preset['C']
        if isinstance(mylinpreset_C, float) :
            mypreset_C=mylinpreset_C*1e-6
        else :
            eval_X=eval(mylinpreset_C)
            目标动能 = eval_X['目标动能']
            储能倍率 = eval_X['储能倍率']

            mypreset_C = 2*目标动能*储能倍率/ (self.lin_preset['U0'] **2)




        #print(f'183:{self.lin_preset}') 
        
        self.preset = {

            'Secondary_parameters':self.lin_preset['Secondary_parameters'],

            "coil_number" : coil_number_calculate ,   # 线圈数量
            
            'coil_layout':self.lin_preset['coil_layout'],        #线圈参数
            'L_coil1':self.lin_preset['L_coil1']*1e-3,     #隔板
            'Inner_diameter':self.lin_preset['Inner_diameter']*1e-3,   #或者可以称之为 线圈内直径
            
            "R_pow" : self.lin_preset['R_pow']*1e-3,     # 电源电阻
            "U0": self.lin_preset['U0'],             # 初始电压  
            "C"  : mypreset_C,       # 电容

            "slug_R" : self.lin_preset['slug_R']*1e-3,       #子弹直径
            "slug_length" : mypreset_slug_length,       #子弹长度
            "m" : mypreset_m,       #子弹质量
            "v0": mypreset_v0,      # 初始速度  
            
            "saturationB_T":self.lin_preset['saturationB_T'],  #饱和磁导
            "阵列规模输入" : self.lin_preset['阵列规模输入'],   # 阵列规模输入
            
        }
        

    #计算结果
    def calculate_result(self,coils,yss,rs,Coil_arguments,模式 = '非参数扫描模式' ):    


        Coil_weight_list=Coil_arguments['Coil_weight_list']
        Coil_R=Coil_arguments['Rs']

        U = yss[-3]
        v = yss[-2]
        t = yss[-1]
        
        self.dEk     = 0.5*self.preset["m"]*(v[-1]**2-self.preset["v0"]**2)  #
        result_use_E = 0.5*(self.preset['C']*(self.preset["U0"]**2-U[-1]**2))
        
        self.eff = self.dEk/(result_use_E) *100
        self.v_result = v[-1] 

        放电深度 = 1 - (U[-1]/ U[0])**2 
        储能倍率= 1/(self.eff/100)/( 放电深度  ) # 1/效率/放电深度
        #储能倍率2 = ( 0.5*(self.preset['C']*(self.preset["U0"]**2)) ) /self.dEk # 储能/动能

        #print(f'放电深度:{放电深度}  储能倍率:{储能倍率} 储能倍率2：{储能倍率2}')





        coil_maxAi=[] #最大电流

        扣除末级线圈计数 = 2
        if self.preset["coil_number"] > 扣除末级线圈计数  :
            need_maxA_number = self.preset["coil_number"] -扣除末级线圈计数
        else :
            need_maxA_number = self.preset["coil_number"]

        for i in range(need_maxA_number):
            coil_maxAi.append(max(yss[i]))
        self.coil_maxA = max(coil_maxAi)
        
        m_C=(0.5*self.preset['C']*self.preset["U0"]**2)/self.preset["Secondary_parameters"]['电容重量储能密度(j/g)']
        Coil_weight_all = sum(Coil_weight_list)
        
        self.m_All=Coil_weight_all+m_C
        

        def calculate_Timing(rs,Coil_arguments) :
            r_mm=np.array(rs)*1000
            r__t=interp1d(r_mm*1e-3,t , kind='linear', bounds_error=False, fill_value="extrapolate")  
            tdtimelist = []
            pwtimelist = []
            for tdi,pwi in zip(Coil_arguments['td'],Coil_arguments['pw']):  
                tdtime = r__t(tdi) *1000000
                edtime = r__t(tdi+pwi) *1000000
                pwtime = edtime - tdtime
                tdtimelist.append( round(tdtime))
                pwtimelist.append( round(pwtime))
                #tdtimelist.append( float(tdtime))
                #pwtimelist.append( float(pwtime))

            return [tdtimelist,pwtimelist]



        if 模式  =='非参数扫描模式' :

            self.Timing= calculate_Timing(rs,Coil_arguments)
            弹丸左侧离隔板左侧距离= ( self.preset["slug_length"] - self.preset["L_coil1"] -  (  rs[0] +0.5*self.preset["slug_length"]+0.5*Coil_arguments['Coil_length'][0] ) )*1e+3

            if self.useSteel_ball ==1: #钢珠
                动子_str= f'钢珠:Φ{self.preset["slug_R"]*1000:.1f},{self.preset["m"]*1000:.2f}g'
            else:
                动子_str= f'定位销:{self.preset["slug_R"]*1000:.1f}*{self.preset["slug_length"]*1000:.1f},{self.preset["m"]*1000:.2f}g'

            self.myresult=f'弹丸左侧离隔板左侧距离:{弹丸左侧离隔板左侧距离} |初始相对位置:{ (rs[0] +0.5*self.preset["slug_length"]+0.5*Coil_arguments['Coil_length'][0] )*1e+3:.1f}mm |初始位置:{rs[0]*1e+3:.1f}mm |\n速度:{self.v_result:.2f}m/s |效率:{self.eff:.1f}% |Δ动能:{self.dEk:.1f}J |{动子_str} |电容:{self.preset["U0"]:.0f}V*{self.preset["C"]*1e+6:.0f}uf,{self.preset["R_pow"]*1e+3:.0f}mΩ |支架直径:{self.preset["Inner_diameter"]*1000:.1f}mm |隔板长度:{self.preset["L_coil1"]*1000:.1f}mm |总重：{Coil_weight_all:.1f}+{m_C:.1f}={self.m_All:.1f}g |储能倍率={  储能倍率 :.1f} |n-2级最大电流：{self.coil_maxA:.1f}A ' #|线圈升温：{delta_T:.2f}°C


            self.delta_T_list =[]
            coili_useE=[sum([ yss[i][j]**2*Coil_R[i]*(t[j]-t[j-1])  for j in range(1,len(t))  ])   for i in range(self.preset["coil_number"])]
            print(f'\n各线圈的电阻耗能: {[ float(round(e,2)) for e in coili_useE]} J')
            print(f'线圈的总电阻耗能: {sum(coili_useE):.2f} J') 
            self.delta_T_list = [ EEi / (wei * 0.385)    for EEi,wei in zip(coili_useE,Coil_weight_list)         ]

            Vb_E=[sum([ yss[i][j]*v[j]*coils[i].Fmodel(rs[j])*(t[j]-t[j-1])  for j in range(1,len(t))  ])   for i in range(self.preset["coil_number"])]
            print(f'\n弹丸动能增量: {[ f'{round(e,2)}' for e in Vb_E]} J')
            print(f'弹丸动能总增量: {sum(Vb_E):.2f} J')

            xioalv=[  100*e0/(e0+e1)      for e0,e1 in  zip(Vb_E ,coili_useE)  ]

            print(f'\n每级效率: {[ f'{round(x,2)}%' for x in xioalv]} J')


            #########
            show_extra_information ='显示额外信息'
            if show_extra_information == '显示额外信息':

                #计算驱动损耗  
                xxx = [0    ,60    ,120, 180  , 240    ,  300     , 360    , 420   ,480  ,600  ,800      ]
                yyy = [0.4 ,1.5    ,2  ,  2.3 ,  2.6    ,   3     ,  3.4    ,3.6   ,3.9  ,5    ,7        ]
                Vf=interp1d(xxx, yyy, kind='linear', bounds_error=False, fill_value="extrapolate") #二极管 电流-压降 曲线
                
                mos_useE=[sum([ yss[i][j]*(Vf(yss[i][j])*2)*(t[j]-t[j-1])  for j in range(1,len(t))  ])   for i in range(self.preset["coil_number"])]
                print(f'\n普通半桥 igbt 总损耗:{sum(mos_useE):.2f} J')
                

        #elif 模式  =='扫描模式' :
        #    self.Timing= calculate_Timing(rs,Coil_arguments)
        elif 模式  =='电压弹长扫描模式' :
            self.Timing= calculate_Timing(rs,Coil_arguments)
            '''弹丸左侧离隔板左侧距离= ( self.preset["slug_length"] - self.preset["L_coil1"] -  (  rs[0] +0.5*self.preset["slug_length"]+0.5*Coil_arguments['Coil_length'][0] ) )*1e+3

            if self.useSteel_ball ==1: #钢珠
                动子_str= f'钢珠:Φ{self.preset["slug_R"]*1000:.1f},{self.preset["m"]*1000:.2f}g'
            else:
                动子_str= f'定位销:{self.preset["slug_R"]*1000:.1f}*{self.preset["slug_length"]*1000:.1f},{self.preset["m"]*1000:.2f}g'

            self.myresult=f'弹丸左侧离隔板左侧距离:{弹丸左侧离隔板左侧距离} |初始相对位置:{ (rs[0] +0.5*self.preset["slug_length"]+0.5*Coil_arguments['Coil_length'][0] )*1e+3:.1f}mm |初始位置:{rs[0]*1e+3:.1f}mm |\n速度:{self.v_result:.2f}m/s |效率:{self.eff:.1f}% |Δ动能:{self.dEk:.1f}J |{动子_str} |电容:{self.preset["U0"]:.0f}V*{self.preset["C"]*1e+6:.0f}uf,{self.preset["R_pow"]*1e+3:.0f}mΩ |支架直径:{self.preset["Inner_diameter"]*1000:.1f}mm |隔板长度:{self.preset["L_coil1"]*1000:.1f}mm |总重：{Coil_weight_all:.1f}+{m_C:.1f}={self.m_All:.1f}g |储能倍率={  储能倍率 :.1f} |n-2级最大电流：{self.coil_maxA:.1f}A ' #|线圈升温：{delta_T:.2f}°C
            self.delta_T_list =[]
            coili_useE=[sum([ yss[i][j]**2*Coil_R[i]*(t[j]-t[j-1])  for j in range(1,len(t))  ])   for i in range(self.preset["coil_number"])]
            print(f'\n各线圈的电阻耗能: {[ float(round(e,2)) for e in coili_useE]} J')
            print(f'线圈的总电阻耗能: {sum(coili_useE):.2f} J') 
            self.delta_T_list = [ EEi / (wei * 0.385)    for EEi,wei in zip(coili_useE,Coil_weight_list)         ]   '''  
        else :
            self.Timing= []



        

    #输入直径，长度来计算子弹质量
    def calculate_cylinder_mass(self,diameter_mm, length_mm, density_g_per_cm3=7.85):  
        # 将直径和长度从毫米转换为厘米  
        diameter_cm = diameter_mm / 10  
        length_cm = length_mm / 10  
          
        # 计算半径  
        radius = diameter_cm / 2  
          
        # 计算体积（单位：立方厘米）  
        volume = math.pi * radius ** 2 * length_cm  
          
        # 计算质量（单位：克）  
        mass_g = density_g_per_cm3 * volume  
        
        mass = round(mass_g ,2)  

        return mass*1e-3 #单位kg



    def calculate_steel_ball_mass(self,diameter_mm,density_g_per_cm3 ):

        # 转换单位
        diameter_cm = diameter_mm / 10  # 毫米转厘米
        radius_cm = diameter_cm / 2     # 计算半径
        
        # 计算体积（立方厘米）
        volume_cm3 = (4/3) * math.pi * (radius_cm ** 3)
        
        # 计算质量（克）
        mass_g = volume_cm3 * density_g_per_cm3
        
        # 转换为千克
        mass_kg = mass_g / 1000
        
        # 输出结果
        print(f"钢珠质量为: {mass_kg:.6f} kg")
        return mass_kg





    def load(self,field,ii=-1):
        if ii==-1: #默认情况下为modei
            ii=self.modei
        
        #print(f'ii:{ii};field:{field} ')
        getValue=self.Modefile.loc[ii,field]
        #print(type(getValue))
        if type(getValue)!= str and math.isnan(getValue) :
            getValue ="空缺"
        elif type(getValue) == np.float64:
            getValue = float(getValue)


        return getValue
    
    #保存数据 以便进Maxwell验证
    def save(self,coils,Timing):   


        #CoilModedict是转换成给Maxwell使用的 数据
        '''CoilModedict={}
        CoilModedict['L_coil0'] = float(coils[0].length *1e+3 )      
        CoilModedict['L_coil1'] = float(self.preset["L_coil1"] *1e+3 )      
        CoilModedict['coil_number'] = self.preset["coil_number"]
        CoilModedict['Inner_diameter'] = float(self.preset["Inner_diameter"]*1e+3 )     
        CoilModedict['R_pow'] = float(self.preset["R_pow"]*1e+3 )    
        CoilModedict['U00'] = float(self.preset["U0"] )   
        CoilModedict['C00'] = float(self.preset["C"]*1e+6 )   
        CoilModedict['slug_R'] = float(self.preset["slug_R"]*1e+3 ) 
        CoilModedict['slug_length'] = float(self.preset["slug_length"]*1e+3 ) 
        CoilModedict['m'] = float(self.preset["m"]*1e+3 ) 
        CoilModedict['v0'] = float(self.preset["v0"] ) 


        stop_time =round(t[-1]*1e+3 +0.2,2)######
        CoilModedict['T'] = float(stop_time)

        CoilModedict['Ns'] = [coili.N for coili in coils ]
        CoilModedict['Rs'] = [ float(coili.R*1e+3) for coili in coils ]
        CoilModedict['Outer_diameter'] = [ float(coili.Outer_d*1e+3) for coili in coils ]

        s_init=  coils[0].tdi #self.td[0]
        td2=[ -s_init+coils[i].tdi     for i in range(self.preset["coil_number"])]
        
        CoilModedict['s_init'] = float( s_init *1e+3 )
        CoilModedict['td'] = [float( td2[i]*1e+3) for i in range(self.preset["coil_number"]) ]
        CoilModedict['pw'] = [float(coils[i].pwi*1e+3) for i in range(self.preset["coil_number"]) ]
  
        try:
            # 保存时序文件  
            with open(script_directory+'\\'+'maxwell.txt', 'w', encoding='utf-8') as file:  
                file.write(str(CoilModedict))  
            print('maxwell.txt 保存成功')   
        except:
            print('maxwell.txt 保存失败')
            
        '''





        #时序
        #转化为c语言可直接使用的二维数组
        tdpw_c=str(Timing)
        tdpw_c = tdpw_c.replace('[', '{') 
        tdpw_c = tdpw_c.replace(']', '}')
        
        print("\n时序")
        print(tdpw_c)
        print("\n")
        ##保存进out_time.h文件 以供单片机使用


        #########
        Array_n, Array_H, Array_L,zlsx = 根据原有数据生成阵列(self.lin_preset["阵列规模输入"])

        if isinstance(self.lin_preset["slug_length"], str) :
            弹长范围 = list(eval(self.lin_preset["slug_length"] ) )
        else :
            弹长范围 = [ self.lin_preset["slug_length"] ]
            
        if isinstance(self.lin_preset["U0"], str) :
            电压范围 = list(eval(self.lin_preset["U0"] ) )
        else :
            电压范围 = [ self.lin_preset["U0"] ]

        时序总数= len(弹长范围) *len(电压范围)
     
        #[{'弹长mm':12,'推弹角度':10,'过渡角度':120,'推弹时间ms':500},{'弹长mm':18,'推弹角度':80,'过渡角度':120,'推弹时间ms':500}]

        弹长mm=[]
        推弹角度=[]
        过渡角度=[]
        推弹时间ms=[]

        for 单片机舵机角度控制 in eval(self.lin_preset["Secondary_parameters"]["单片机舵机角度控制"]):
            弹长mm.append(int(单片机舵机角度控制['弹长mm']))
            推弹角度.append(int(单片机舵机角度控制['推弹角度']))
            过渡角度.append(int(单片机舵机角度控制['过渡角度']))
            推弹时间ms.append(int(单片机舵机角度控制['推弹时间ms']))

        舵机模式= [ 弹长mm,推弹角度,过渡角度,推弹时间ms   ]


        #
        单片机adc转换电容电压 = eval(self.lin_preset["Secondary_parameters"]['单片机adc转换电容电压'])
        Tnum = str(len(Timing[0]) )

        ######### 
        show_c= f'''#define Tnum { Tnum   }
#define Array_n {Array_n}
#define Array_H {Array_H}
#define Array_L {Array_L}

float projectile[] = { str(弹长范围).replace('[', '{').replace(']', '}')   };
float voltage[] =    { str(电压范围).replace('[', '{').replace(']', '}')   };

float inter_data[2][{len(单片机adc转换电容电压[0])}]= { str( 单片机adc转换电容电压 ).replace('[', '{').replace(']', '}')        };
int duojiMode[4][{len(舵机模式[0])}]=   { str(舵机模式).replace('[', '{').replace(']', '}')   };


int Timing[{2*时序总数}][{Tnum}] = { str(tdpw_c).replace('[', '{').replace(']', '}')    };
int zlsx[{Tnum}][3] = { str(zlsx).replace('[', '{').replace(']', '}') };
'''








        #show_c = show_c

        
        try:
            # 保存时序文件  
            with open(script_directory+'\\'+'out_time.h', 'w', encoding='utf-8') as file:  
                file.write(show_c)  
            print('时序文件out_time.h 保存成功')   
        except:
            print('时序文件out_time.h 保存失败')



        #global combo_input_文件地址 

        #时序文件输出地址 = combo_input_文件地址.get()

        try:

            combo_input_文件地址.set( f'{ mode_次要参数['时序文件输出地址'] }'   )  # 直接设置显示的值
            时序文件输出地址 = combo_input_文件地址.get()
            # 保存时序文件  
            with open(时序文件输出地址+'\\'+'out_time.h', 'w', encoding='utf-8') as file:  
                file.write(show_c)  
            print(f'时序文件{时序文件输出地址}\\out_time.h 保存成功')
        except:
            print(f'时序文件输出地址\\out_time.h 保存失败')

        #########
        try :
            global combo_input_timing
            global combo_input_受力系数
            global combo_input_初始位置
            global combo_input_电压范围
            global combo_input_弹丸长度范围
            global combo_input_时序文件
            if self.input_Timing ==[] : #位移控制时 自动填值 ，以便进行时间控制
                combo_input_timing.set(f'{Timing}'.replace("[", "{").replace("]", "}")    )  # 直接设置显示的值
                combo_input_受力系数.set( f'{self.lin_preset["Secondary_parameters"]['受力系数']}'   )  # 直接设置显示的值
                combo_input_初始位置.set( f'{coils[0].tdi *1e+3:.1f}'   )  # 直接设置显示的值
                combo_input_电压范围.set( f'{电压范围}'   )  # 直接设置显示的值
                combo_input_弹丸长度范围.set( f'{弹长范围}'   )  # 直接设置显示的值

                combo_input_时序文件.delete("1.0", tk.END)
                combo_input_时序文件.insert(tk.END,  f'{show_c}') 



        except Exception as e:
            print(f"e \n \n")  


        #self.Modefile.loc[self.modei,"result"]= str(self.myresult) + f"| 总加速时间:{stop_time}  "
        # 将修改后的数据保存进CSV文件  
        #global modecsv
        #self.Modefile.to_csv(modecsv, index=False, encoding='gbk')


#线圈数据
class Coil():
    def __init__(self,i,Coil_arguments,Mode) -> None:
    
        self.μ0 = 4 * pi * 1e-7
        self.i = i   #线圈编号
        self.N = Coil_arguments['Ns'][i]   #线圈匝数
        self.L = Coil_arguments['Ls'][i]  #线圈自感
        self.M = Coil_arguments['Ms'][i]  #线圈互感
        self.R = Coil_arguments['Rs'][i]  #线圈电阻
        
        self.length = Coil_arguments['Coil_length'][i]  #线圈长度
        self.Inner_d = Mode.preset['Inner_diameter']  #线圈内直径
        self.Outer_d = Coil_arguments['Outer_diameter'][i]  #线圈外直径
        self.r = Coil_arguments['coil_location'][i]    #线圈位置s
        self.tdi = Coil_arguments['td'][i]  #线圈导通位置
        self.pwi = Coil_arguments['pw'][i]  #线圈导通持续距离

 
        if Mode.input_Timing ==[] :
            self.td_t  = -1  #线圈导通时刻
            self.pw_t = -1  #线圈导通持续时间 

        else :
            self.td_t  = Mode.input_Timing[0][i] *1e-6  #线圈导通时刻
            self.pw_t = Mode.input_Timing[1][i] *1e-6 #线圈导通持续时间



        self.slug_halflength = (Mode.preset['slug_length']/2)

        self.useSteel_ball = Mode.useSteel_ball
        self.dx= Mode.preset['slug_length']/Mode.preset["Secondary_parameters"]['钢珠切分数量']

        self.slug_r = Mode.preset['slug_R']/2  #弹丸半径
        self.S = pi*self.slug_r**2   #弹丸端面的面积


        受力系数列表 = Mode.preset["Secondary_parameters"]['受力系数']
        #print(len(受力系数列表))

        if i < len(受力系数列表):
            受力系数 = 受力系数列表[i]
        else :
            受力系数 = 受力系数列表[-1]
        #print(f'{i}||受力系数:{受力系数}')
        
        self.coil_M = 受力系数* Mode.preset['saturationB_T']  /self.μ0 #弹丸磁化强度 ！     


        
    def BFieldCoil(self, z, current=1):   #根据位置 计算磁感应强度！（current=1 代表工作在 单位电流时的情况）
        L=self.length
        a=L/2
        r2=self.Outer_d/2
        r1=self.Inner_d/2
        R=r2-r1
        def F(x):  
            return x * math.log((sqrt(x ** 2 + r2 ** 2) + r2) /  
                                  (sqrt(x ** 2 + r1 ** 2) + r1))  
          
        return ( self.μ0*self.N*current/(2*L*R) ) * (F(z+a)-F(z-a)) 
        

    def calculate_F(self,D):      #根据位置与磁感应强度差 计算弹丸受力！
        B0=self.BFieldCoil(D+self.slug_halflength)
        B1=self.BFieldCoil(D-self.slug_halflength)
        ΔB= B0-B1
        return self.coil_M*ΔB*self.S


    def calculate_slice_area(self, h):
        R = self.slug_halflength
        #print(f'R {R}| h {h} ')


        if abs(h) > R:
            return 0  # 超出球的范围，切片不存在
        r = math.sqrt(R**2 - h**2)
        area = math.pi * r**2
        #print(f'r {r}| h:{h}  | area:{area} ')
        return area


    def calculate_F_珠子(self,D): 

        Flist=[]
        #对弹丸区域进行细分，计算每个细分内的M与ΔΦ
        for x in np.arange(D-self.slug_halflength,D+self.slug_halflength,self.dx):
            B0=self.BFieldCoil(x+self.dx)
            B1=self.BFieldCoil(x)
            ΔBi= (B0-B1) 

            S0= self.calculate_slice_area(x-D)
            #print(f'D {D} x:{x} S0 {S0} self.S {self.S}')
            Flist.append(self.coil_M*ΔBi*S0)
        
        #print(Flist)
        return sum(Flist) #计算F




    def Fmodel(self,r):
        #r-self.r 是弹丸中心到线圈中心的距离
        if  self.useSteel_ball ==0:
            return self.calculate_F(r-self.r) 
        else :
            return self.calculate_F_珠子(r-self.r) 
        



#子弹运动
class Slug_move():
    def __init__(self,Mode,coils,step_select) -> None:
        if Mode.input_Timing ==[] :
            self.r=coils[0].tdi     #初始弹丸位置r为td[0],是个负数
        else:
            global combo_input_初始位置 
            self.r= float(combo_input_初始位置.get() )*1e-3

        self.R_all= -self.r + coils[-1].r+coils[-1].length  # 总加速距离   

        self.drset= Mode.preset["Secondary_parameters"]['默认后续微元步进']*1e-3        #0.2 *1e-3  
            
        drlist_head_eval = eval(Mode.preset["Secondary_parameters"]['初始微元步进切分'] )  #eval('[[0.001]*10 ,[0.01]*10,[0.1]*80]')    
        drlist_head=[item*1e-3 for sublist in drlist_head_eval for item in sublist]        
        n=int((self.R_all-sum(drlist_head))/self.drset)
        self.drlist= drlist_head  + [self.drset]*n
        #print(self.drlist)    
        

        self.t=0
        self.Mode = Mode
        self.coils = coils
        
        
        self.SwitchResult=[-1 for coil in self.coils]     #计算各线圈的导通状态
        self.FmodelResult=[0 for coil in self.coils ]     #计算各线圈的单位电流受力
        
        self.leftopencoil = 0  #最左侧导通线圈编号 
        self.lastopencoil = 0  #最右侧导通线圈编号
        #计算驱动损耗  
        xxx = [0    ,60    ,120, 180  , 240    ,  300     , 360    , 420   ,480  ,600  ,800   ,1000   ]
        yyy = [0.4 ,1.5    ,2  ,  2.3 ,  2.6    ,   3     ,  3.4    ,3.6   ,3.9  ,5    ,7     ,9   ]
        self.Vf=interp1d(xxx, yyy, kind='linear', bounds_error=False, fill_value="extrapolate") #二极管 电流-压降 曲线
        

        if Mode.preset["阵列规模输入"] ==[]:
            self.驱动损耗系数=2
        else:
            self.驱动损耗系数=3




    def calculate_air_resistance(self,radius, velocity, cd, rho=1.225): #计算空气阻力
        """
        计算球体在空气中运动时所受到的空气阻力
        
        参数:
            radius (float): 球体的半径（单位：米）
            velocity (float): 球体的速度（单位：米/秒）
            rho (float): 空气密度（默认值：1.225 kg/m³）
            cd (float): 阻力系数（默认值：0.47，适用于球体） ，0.82定位销
        
        返回:
            float: 空气阻力（单位：牛顿）
        """

        area = math.pi * radius ** 2  # 迎风面积
        force = 0.5 * rho * velocity ** 2 * cd * area
        return force
       
    #RK4（4阶龙格库塔法），算出下一个 ys
    def rk4_step(self,equ,ys,dr):
        k1 = equ(ys)
        k2 = equ(ys+k1*dr/2)
        k3 = equ(ys+k2*dr/2)
        k4 = equ(ys+k3*dr)
        return ys+dr*(k1+2*k2+2*k3+k4)/6
        
    # main
    def MainEquation(self,ys):
        devi = np.zeros_like(ys)
        ## ys = [I1,I2 ... INcoils,U,v,t]
        ys[:len(self.coils)] = np.where(ys[:len(self.coils)]>0,ys[:len(self.coils)],0)
        U,v,self.t = ys[-3:]
        self.v=v
        
        #现在只动态计算动子附近的线圈
        for i in range(len(self.coils)):
            if i > 0 and ys[i]>0  :
                self.leftopencoil = i-1
                break   

        
        left  = self.leftopencoil
        right = self.lastopencoil+2
        if right > len(self.coils):
            right = len(self.coils)



        if self.Mode.input_Timing !=[] : #时间顺序
            self.SwitchResult[left:right] = [self.StateModel_t(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        else:
            self.SwitchResult[left:right] = [self.StateModel(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        
        self.FmodelResult[left:right] = [self.coils[i].Fmodel(self.r)  for i in range(left,right)]     #动态计算各线圈的单位电流受力
 
 
        coilsneed = [ self.coils[i]  for i in range( self.leftopencoil ,self.lastopencoil+1) ]       
        coilsneed_len= len(coilsneed)
        #print([ i  for i in range( self.leftopencoil ,self.lastopencoil+1) ])     
     
        equations = np.zeros((coilsneed_len,coilsneed_len))
        equations2 = np.zeros(coilsneed_len)
 
        V_pow = U - sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed])*self.Mode.preset["R_pow"]

        for i,coili in zip(range(coilsneed_len),coilsneed):
 
            if self.SwitchResult[coili.i]>0 or ys[coili.i]>0:
                if i > 2:
                    equations[i][i-2] = coili.M['-2']
                if i > 1:
                    equations[i][i-1] = coili.M['-1']
                equations[i][i] = coili.L
                if i < coilsneed_len-1:
                    equations[i][i+1] = coili.M['1']
                if i < coilsneed_len-2:
                    equations[i][i+2] = coili.M['2'] 
                    
                equations2[i]   =  - ys[coili.i]*coili.R  \
                                    + self.SwitchResult[coili.i]*V_pow \
                                    - self.驱动损耗系数*self.Vf(ys[coili.i])\
                                    - v*self.FmodelResult[coili.i]   
                                      
            else:
                 equations[i][i] = 1
    
        devi[coilsneed[0].i:coilsneed[-1].i+1] = (np.linalg.solve(equations,equations2))/v  #dI/dr

        devi[-3] = (- sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed]) / self.Mode.preset["C"])/v  #dU/dr

        if  self.Mode.useSteel_ball ==0: #计算定位销阻力
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.82)
        else : #珠子
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.47 )

        devi[-2] = (   (sum([ys[coil.i]*self.FmodelResult[coil.i] for coil in coilsneed]) -air_resistan   )/self.Mode.preset["m"])/v #dv/dr
        devi[-1] = 1/v   # dr

        return devi  #这里输出的是dys/dr,后续在rk4那乘以dr就能得到dys，再ys+dys就能求出下一步的ys (ys为各种变量如U,v,i等)
    
    def MainEquation_boost(self,ys):
        devi = np.zeros_like(ys)
        ## ys = [I1,I2 ... INcoils,U,v,t]
        ys[:len(self.coils)] = np.where(ys[:len(self.coils)]>0,ys[:len(self.coils)],0)
        Uall= ys[ -3 - len(self.coils) : -3]
        分路数量 = self.Mode.lin_preset["分路数量"]


        v,self.t = ys[-2:]

        self.v=v
        
        #现在只动态计算动子附近的线圈
        for i in range(len(self.coils)):
            if i > 0 and ys[i]>0  :
                self.leftopencoil = i-1
                break   

        
        left  = self.leftopencoil
        right = self.lastopencoil+2
        if right > len(self.coils):
            right = len(self.coils)



        if self.Mode.input_Timing !=[] : #时间顺序
            self.SwitchResult[left:right] = [self.StateModel_t(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        else:
            self.SwitchResult[left:right] = [self.StateModel(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        
        self.FmodelResult[left:right] = [self.coils[i].Fmodel(self.r)  for i in range(left,right)]     #动态计算各线圈的单位电流受力
 
 
        coilsneed = [ self.coils[i]  for i in range( self.leftopencoil ,self.lastopencoil+1) ]       
        coilsneed_len= len(coilsneed)
        #print([ i  for i in range( self.leftopencoil ,self.lastopencoil+1) ])     
     
        equations = np.zeros((coilsneed_len,coilsneed_len))
        equations2 = np.zeros(coilsneed_len)
 

        for i,coili in zip(range(coilsneed_len),coilsneed):
 
            if self.SwitchResult[coili.i]>0 or ys[coili.i]>0:
                if i > 2:
                    equations[i][i-2] = coili.M['-2']
                if i > 1:
                    equations[i][i-1] = coili.M['-1']
                equations[i][i] = coili.L
                if i < coilsneed_len-1:
                    equations[i][i+1] = coili.M['1']
                if i < coilsneed_len-2:
                    equations[i][i+2] = coili.M['2'] 
                    
                V_pow = Uall[coili.i] - sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed])* self.CRall[coili.i]


                if  self.SwitchResult[coili.i] ==1 : #导通阶段
                    if V_pow <= 0:  #电容电压小于0 ，就通过二极管续流
                        V_pow =0
                    equations2[i]   =  - ys[coili.i]*coili.R  \
                                        + self.SwitchResult[coili.i]*V_pow \
                                        - self.驱动损耗系数*self.Vf(ys[coili.i])\
                                        - v*self.FmodelResult[coili.i]   
                else : #回收阶段
                    equations2[i]   =  - ys[coili.i]*coili.R  \
                                        + self.SwitchResult[coili.i]*V_pow \
                                        - self.驱动损耗系数*self.Vf(ys[coili.i])\
                                        - v*self.FmodelResult[coili.i]\
                                        - max(Uall)     


            else:
                 equations[i][i] = 1
    
        devi[coilsneed[0].i:coilsneed[-1].i+1] = (np.linalg.solve(equations,equations2))/v  #dI/dr



        #devi[-3] = (- sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed]) / self.Mode.preset["C"])/v  #dU/dr

        for j in range( 分路数量):  #分路处理
            coilsneed2 =[ coil   for coil in coilsneed   if coil.i%分路数量 == j ] 

            for coilneed2 in coilsneed2:
                C2 = self.Call[coilneed2.i]
                coils_len =len(self.coils)
                if self.SwitchResult[coilneed2.i] > 0  :  #导通阶段
                    if Uall[coilneed2.i] >0:   #电容电压大于0时进行，小于0 就通过二极管续流
                        coil_I= ys[coilneed2.i]
                        devi[  coils_len+coilneed2.i] =  ( - coil_I  / C2    )  /v  #dU2/dr  =C2/I


                elif self.SwitchResult[coilneed2.i] < 0 and  ys[coilneed2.i] >0:

                    coil_I= ys[coilneed2.i]
                    devi[  coils_len+coilneed2.i] =  ( - coil_I  / C2    )  /v  # 本线圈
                    
                    try:
                        coilsneed3 = [ self.coils[i]  for i in range( coilneed2.i ,coils_len)   if i%分路数量 == j ]      
                        coilsneed3 = coilsneed3[1:] #扣除第一个线圈
                        coilsneed3len= len(coilsneed3) 
                        for coilneed3 in coilsneed3 :
                            devi[  coils_len+coilneed3.i] =  ( (coil_I/coilsneed3len)  / self.Call[coilneed3.i]    )  /v  # 回收电容



                        #coilnexti=  coilneed2.i + 分路数量
                        #devi[  coils_len+coilnexti] =  ( coil_I  / self.Call[coilnexti]    )  /v  # 本线圈的下一级 回收电容


                    except:
                        pass
        
        



        if  self.Mode.useSteel_ball ==0: #计算定位销阻力
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.82)
        else : #珠子
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.47 )

        devi[-2] = (   (sum([ys[coil.i]*self.FmodelResult[coil.i] for coil in coilsneed]) -air_resistan   )/self.Mode.preset["m"])/v #dv/dr
        devi[-1] = 1/v   # dr

        return devi  #这里输出的是dys/dr,后续在rk4那乘以dr就能得到dys，再ys+dys就能求出下一步的ys (ys为各种变量如U,v,i等)
    
    # main
    def MainEquation_全桥谐振(self,ys):
        devi = np.zeros_like(ys)
        ## ys = [I1,I2 ... INcoils,U,v,t]
        ys[:len(self.coils)] = np.where(ys[:len(self.coils)]>0,ys[:len(self.coils)],0)
        U,v,self.t = ys[-3:]

        分路数量=   self.分路数量
        U2 = ys[ -3-分路数量 :  -3 ]
 
        self.v=v
        
        #现在只动态计算动子附近的线圈
        for i in range(len(self.coils)):
            if i > 0 and ys[i]>0  :
                self.leftopencoil = i-1
                break   

        
        left  = self.leftopencoil
        right = self.lastopencoil+2
        if right > len(self.coils):
            right = len(self.coils)




        #self.SwitchResult[left:right] = [self.StateModel_全桥谐振(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        
        if self.Mode.input_Timing !=[] : #时间顺序
            self.SwitchResult[left:right] = [self.StateModel_t(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        else:
            self.SwitchResult[left:right] = [self.StateModel(self.coils[i]) for i in range(left,right)]    #动态计算各线圈的导通状态
        

        self.FmodelResult[left:right] = [self.coils[i].Fmodel(self.r)  for i in range(left,right)]     #动态计算各线圈的单位电流受力
 
 
        coilsneed = [ self.coils[i]  for i in range( self.leftopencoil ,self.lastopencoil+1) ]       
        coilsneed_len= len(coilsneed)
        #print([ i  for i in range( self.leftopencoil ,self.lastopencoil+1) ])     
               
        equations = np.zeros((coilsneed_len,coilsneed_len))
        equations2 = np.zeros(coilsneed_len)
 
        V_pow = U - sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed])*self.Mode.preset["R_pow"]


        for i,coili in zip(range(coilsneed_len),coilsneed):
 
 

        
            if coili.tdi <= self.r and (ys[coili.i] >0 or coili.i >= self.lastopencoil )   :  #判断子弹进入了 线圈i在导通区间   and (I >0 or coil.i >= self.lastopencoil )
                if coili.i > self.lastopencoil :
                    self.lastopencoil = coili.i    #动态记录的右侧线圈编号
                self.SwitchResult[coili.i]= 1   # 线圈i 导通阶段
            else:
                self.SwitchResult[coili.i]=  0




            if self.SwitchResult[coili.i]>0 or ys[coili.i]>0:
                if i > 2:
                    equations[i][i-2] = coili.M['-2']
                if i > 1:
                    equations[i][i-1] = coili.M['-1']
                equations[i][i] = coili.L
                if i < coilsneed_len-1:
                    equations[i][i+1] = coili.M['1']
                if i < coilsneed_len-2:
                    equations[i][i+2] = coili.M['2'] 





                if((coili.i - coili.i%分路数量 )/分路数量)%2 == 0 :

                    equations2[i]   =  - ys[coili.i]*coili.R  \
                                        + self.SwitchResult[coili.i]*V_pow \
                                        - self.驱动损耗系数*self.Vf(ys[coili.i])\
                                        - v*self.FmodelResult[coili.i]   \
                                        +U2[coili.i%分路数量]

                else :  
                    if  self.Mode.lin_preset['topology'] == '补能buck谐振' :
                        V_pow = 0
                    equations2[i]   =  - ys[coili.i]*coili.R  \
                                        + self.SwitchResult[coili.i]*V_pow \
                                        - self.驱动损耗系数*self.Vf(ys[coili.i])\
                                        - v*self.FmodelResult[coili.i]   \
                                        - U2[coili.i%分路数量]


            else:
                 equations[i][i] = 1

        devi[coilsneed[0].i:coilsneed[-1].i+1] = (np.linalg.solve(equations,equations2))/v  #dI/dr

        devi[-3] = (- sum([ys[coil.i]      for coil in coilsneed]) / self.Mode.preset["C"]  )/v  #dU1/dr  =C1/I 

        for j in range( 分路数量):  #分路计算
            coilsneed2 =[ coil   for coil in coilsneed   if coil.i%分路数量 == j ] 
            devi[-3-分路数量+j] = (- sum([ys[coil.i] if ((coil.i-j)/分路数量)%2 == 0   else  -ys[coil.i]     for coil in coilsneed2]) / self.C2    )/v  #dU2/dr  =C2/I
 


        #devi[-3] = (- sum([self.SwitchResult[coil.i]*ys[coil.i] for coil in coilsneed]) / self.Mode.preset["C"])/v  #dU/dr

        if  self.Mode.useSteel_ball ==0: #计算定位销阻力
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.82)
        else : #珠子
            air_resistan=self.calculate_air_resistance(self.Mode.preset['slug_R']/2 , v , 0.47 )



        devi[-2] = (   (sum([ys[coil.i]*self.FmodelResult[coil.i] for coil in coilsneed]) -air_resistan   )/self.Mode.preset["m"])/v #dv/dr
        devi[-1] = 1/v   #

        return devi  #这里输出的是dys/dr,后续在rk4那乘以dr就能得到dys，再ys+dys就能求出下一步的ys (ys为各种变量如U,v,i等)
    


    def main(self):

        rs = []
        rs.append(self.r)


        if self.Mode.lin_preset['topology'] == '半桥' :

            ys = np.zeros(self.Mode.preset["coil_number"] + 3 )
            ys[-3] = self.Mode.preset["U0"]
            ys[-2] = self.Mode.preset["v0"]  
            ys[-1] = 0                  #初始t=0 
            yss = []
            yss.append(ys)



            for dr in tqdm.tqdm(self.drlist):
            #for dr in self.drlist:
                ys = self.rk4_step(self.MainEquation,ys,dr)  #,(self.coils,self.Mode.preset)
                yss.append(ys)
                self.r = self.r+dr
                rs.append(self.r)


        elif self.Mode.lin_preset['topology'] == 'boost' :

            ys = np.zeros(2*self.Mode.preset["coil_number"] + 3   )
            U2=[]
            self.Call =[]
            self.CRall =[]
            for coil_layout in self.Mode.preset["coil_layout"]:
                for coili in range(coil_layout['线圈数量']  ):
                    self.Call.append(coil_layout['电容值(uf)'] *1e-6)
                    self.CRall.append(coil_layout['外部阻抗(esr)'] *1e-3)

                    U2.append(self.Mode.preset["U0"])

            self.Mode.preset["C"]  =sum(self.Call)

            for j in range(self.Mode.preset["coil_number"] , 2*self.Mode.preset["coil_number"]) :
                ys[j] = U2[j- self.Mode.preset["coil_number"]  ]


            ys[-2] = self.Mode.preset["v0"]  
            ys[-1] = 0                  #初始t=0 
            yss = []
            yss.append(ys)

            for dr in tqdm.tqdm(self.drlist):
            #for dr in self.drlist:
                ys = self.rk4_step(self.MainEquation_boost,ys,dr)  #,(self.coils,self.Mode.preset)
                yss.append(ys)
                self.r = self.r+dr
                rs.append(self.r)

            #重新计算对应的能量
            Uall = yss[-1][self.Mode.preset["coil_number"] :2*self.Mode.preset["coil_number"]]
            Uall_e = 0
            for UU,UC in zip(Uall,self.Call):
                Uall_e +=  0.5*UC*UU **2

            #yss[-1][-3]=  #(2*Uall_e/sum(self.Call) )**0.5

            #print(yss[-1][-3])



        elif self.Mode.lin_preset['topology'] == '全桥谐振' or  self.Mode.lin_preset['topology'] == '补能buck谐振' :
            self.分路数量 = self.Mode.lin_preset['分路数量']
            self.C2    = self.Mode.lin_preset['C2']  *1e-6

            ys = np.zeros(self.Mode.preset["coil_number"] + 3 +self.分路数量 )
            for j in range( self.分路数量):
                ys[ -3-self.分路数量 +j ] =0


            ys[-3] = self.Mode.preset["U0"]
            ys[-2] = self.Mode.preset["v0"]  
            ys[-1] = 0                  #初始t=0 
            yss = []
            yss.append(ys)

            for dr in tqdm.tqdm(self.drlist):
            #for dr in self.drlist:
                ys = self.rk4_step(self.MainEquation_全桥谐振,ys,dr)  #,(self.coils,self.Mode.preset)
                yss.append(ys)
                self.r = self.r+dr
                rs.append(self.r)




        yss = np.array(yss).T  #.T 是把列变成行
        rs  = np.array(rs)
        return yss,rs
      
        
    #######################线圈i状态切换（导通/回收）
    def StateModel(self,coil):  #Switch函数 #判断线圈coil.i是否导通 
        if coil.tdi <= self.r < coil.tdi+coil.pwi :  #判断子弹进入了 线圈i在导通区间
            if coil.i > self.lastopencoil :
                self.lastopencoil = coil.i    #动态记录的右侧线圈编号
            return 1   # 线圈i 导通阶段
        else:
            return -1
        
    def StateModel_全桥谐振(self,coil,I=0):  #Switch函数 #判断线圈coil.i是否导通 

        
        if coil.tdi <= self.r and (I >0 or coil.i >= self.lastopencoil )   :  #判断子弹进入了 线圈i在导通区间   and (I >0 or coil.i >= self.lastopencoil )
            if coil.i > self.lastopencoil :
                self.lastopencoil = coil.i    #动态记录的右侧线圈编号
            return 1   # 线圈i 导通阶段
        else:
            return 0

        
    def StateModel_t(self,coil):  #Switch函数 #判断线圈coil.i是否导通 
        
        if coil.td_t <= self.t < coil.td_t+coil.pw_t :  #判断子弹进入了 线圈i在导通区间
            if coil.i > self.lastopencoil :
                self.lastopencoil = coil.i    #动态记录的右侧线圈编号
            return 1   # 线圈i 导通阶段
        else:
            return -1




### 计算 api
def coilgunApi(perset, 模式='ai优化模式',show_coil=''):

    Mode=CoilMode(0,perset)
    Mode.lin_preset_To_preset()

    Coil_arguments =calculate_all(Mode,show_coil)
    coils = [ Coil(i,Coil_arguments   ,Mode)  for i in range(Mode.preset["coil_number"])]
    yss,rs=Slug_move(Mode,coils,1).main()
    Mode.calculate_result(coils,yss,rs,Coil_arguments,模式)

    if 模式=='电压弹长扫描模式':
        return [ float(Mode.dEk),float(Mode.coil_maxA) ,float(Mode.eff ), float(Mode.v_result), Mode.m_All , Mode.Timing ,coils,yss,rs  , Coil_arguments , Mode] #copy.deepcopy( Mode.lin_preset )
    elif 模式 == '非参数扫描模式':
        return [ float(Mode.dEk),float(Mode.coil_maxA) ,float(Mode.eff ), float(Mode.v_result), Mode.m_All , Mode.Timing ,coils,yss,rs  , Coil_arguments , Mode ] #copy.deepcopy( Mode.lin_preset )
    else:
        return [float(Mode.dEk),float(Mode.coil_maxA) ,float(Mode.eff ), float(Mode.v_result), Mode.m_All] #



# ------- *  图表展示 * ---------

def figure_show(coils,yss,rs  , Coil_arguments , Mode , combovalue={'showplt':'仅显示位移图'}):

    U = yss[-3]
    v = yss[-2]
    t = yss[-1]

    fig=plt.figure()

    bx5 = fig.add_subplot(311)
    bx5.set_ylabel("动子运动")

    bx5.set_title(f'{Mode.lin_preset['only_id']} \n' +Mode.myresult) 

    class 线圈参数重整:
        def __init__(self, Mode,Coil_arguments,move_result):
            self.Mode = Mode
            self.Coil_arguments =Coil_arguments
            self.rs,self.U,self.v,self.t    =move_result

        def on_move(self,event):  
            if event.inaxes == bx5 or event.inaxes == bx1:  # 检查事件是否发生在特定的子图ax1上  
                #print(f'Mouse position on ax1: ({event.xdata:.2f}, {event.ydata:.2f})')  
                global slug_r_glaobal
                slug_r_glaobal = event.xdata*1e-3
            else :  
                slug_r_glaobal =self.Coil_arguments['td'][0] 

        def get_coili(self, event):
            coili=0
            for i in range(self.Mode.preset["coil_number"]):
                if self.Coil_arguments['coil_location'][i] - 0.5*self.Coil_arguments['Coil_length'][i] - self.Mode.preset['L_coil1']  < event.xdata*1e-3  <self.Coil_arguments['coil_location'][i] + 0.5*self.Coil_arguments['Coil_length'][i]  :
                    coili=i 
                    break

            Coili_argument ={}
            for key, value in self.Coil_arguments.items(): 
                Coili_argument[key] = [value[coili]]
            
            return coili,Coili_argument



        def on_click(self, event):

            def print_input():
                # 获取文本框的全部内容（从第1行第1列到最后一行）
                bandwidth = 脉宽_entry.get("1.0", tk.END).strip()  # .strip() 去除末尾换行符
                print(f"输入的带宽是:\n{bandwidth}")
            # 创建主窗口

            if  event.inaxes == bx5 and event.button == 3:  # 右键点击  
                coili,Coili_argument=self.get_coili(event)
                线圈参数显示={}
                线圈参数显示['线圈编号'] = coili
                线圈参数显示['匝数'] = self.Coil_arguments['Ns'][coili]
                i=coili
                线圈参数显示['线圈信息'] = f"线圈编号：{i} |匝数：{int(self.Coil_arguments['Ns'][i])} ={self.Coil_arguments['Coil_pq'][i][0]}*{self.Coil_arguments['Coil_pq'][i][1]}+{int(self.Coil_arguments['Coil_pq'][i][2])} |自感：{int(self.Coil_arguments['Ls'][i]*1e+6)}uh |阻值：{int(self.Coil_arguments['Rs'][i]*1000)}mΩ |外径：{self.Coil_arguments['Outer_diameter'][i]*1000 :.1f}mm |线圈厚度：{(( self.Coil_arguments['Outer_diameter'][i] - self.Mode.preset['Inner_diameter'] )/2)*1000 :.1f}mm |重量：{self.Coil_arguments['Coil_weight_list'][i]:.2f}g "


                线圈参数显示['触发起始位置(导通时弹丸中心距线圈中心距离)'] = round( (self.Coil_arguments['td'][coili] -self.Coil_arguments['coil_location'][coili])*1000,2)
                线圈参数显示['脉宽(持续导通距离)'] = round(self.Coil_arguments['pw'][coili] *1000,2)
                线圈参数显示['触发终止位置(关断时弹丸中心距线圈中心距离)'] = round( (self.Coil_arguments['td'][coili] +self.Coil_arguments['pw'][coili]-self.Coil_arguments['coil_location'][coili] ) *1000   ,2)
                线圈参数显示['关断策略偏移(关断时弹丸前端距线圈中心距离)'] = round( (self.Coil_arguments['td'][coili] +self.Coil_arguments['pw'][coili]-self.Coil_arguments['coil_location'][coili] +0.5*self.Mode.preset['slug_length']   ) *1000   ,2)

                root = tk.Tk()
                #root.title("带宽输入界面")

                # 创建一个标签
                for key, value in 线圈参数显示.items(): 
                    tk.Label(root, text=f"{key}:  {value}" , anchor="w").pack(fill="x")



                # 创建一个多行文本框（高度10行，宽度40字符）
                #text = tk.Text(root, height=10, width=40, wrap=tk.WORD)  # wrap=tk.WORD 自动换行
                #text.pack(pady=5, padx=10)  # padx 设置水平间距
                #text.insert("1.0", str(线圈参数显示))

                脉宽_entry = tk.Entry(root)
                脉宽_entry.pack(pady=5)


                # 创建一个按钮，点击时调用print_input函数
                button = tk.Button(root, text="打印输入内容", command=print_input)
                button.pack(pady=10)

                # 运行主循环
                #root.mainloop()
                print(f"Clicked at: {Coili_argument}")
                print("Custom Mode:", self.Mode)


    # 通过实例方法传递局部变量
    handler = 线圈参数重整(Mode,Coil_arguments,(rs,U,v,t))
    bx5.figure.canvas.mpl_connect('button_press_event', handler.on_click)
    bx5.figure.canvas.mpl_connect('motion_notify_event', handler.on_move)  

    bx1 = fig.add_subplot(312, sharex = bx5)
    bx1.set_ylabel("线圈电流 / A")
     
    bx2 = fig.add_subplot(337, sharex = bx5)
    bx2.set_ylabel("力 / N")

    bx3 = fig.add_subplot(338, sharex = bx5)
    bx3.set_ylabel("电容电压 / V")

    bx4 = fig.add_subplot(339, sharex = bx5) 
    
    bx4.set_ylabel("动子速度 / m/s")

    
    bx3.set_xlabel(f"位置 / mm    行波加速模拟器   版本号为：{__version__}   开源地址：https://www.kechuang.org/t/90279  作者：gugugu")
    r_mm=np.array(rs)*1000


    for i in range(Mode.preset["coil_number"]):
        bx1.plot(r_mm,yss[i])
        
    tdnp=np.array( [ round(tdi*1000,2) for tdi in Coil_arguments['td']] )
    pwnp=np.array( [ round(pwi*1000,2) for pwi in Coil_arguments['pw']] )
    ednp=tdnp+pwnp

    coil_loc=np.array( [ round(coili.r*1000,2) for coili in coils] )

    coil_num = [i  for i in range(Mode.preset["coil_number"])]
    cnumnp=np.array( coil_num )

    #对电流图做标记 
    for tdi,edi,coil_loci,cnumi in zip(tdnp, ednp,coil_loc,cnumnp):  
        bx1.text(edi, max(yss[cnumi]), str(cnumi), color='green', ha='center', va='bottom') #, va='bottom'
        bx1.text(coil_loci, 0, str(cnumi), color='blue', ha='center') #, va='bottom'
        
    ###########
    #bx2.plot(r_mm[1:],(v[1:]*(v[1:]-v[:-1])/ (rs[1:]-rs[:-1])   )*Mode.preset["m"]) #f=ma  ，计算力的第二种方法,a=v*dv/dr,a=dv/dt
    bx2.plot(r_mm[1:],((v[1:]-v[:-1])/ (t[1:]-t[:-1])   )*Mode.preset["m"]) #f=ma  ，计算力的第二种方法,a=v*dv/dr,a=dv/dt

        
    '''V1=v[-1]
    X1=rs[-1]
    C1=sqrt(V1 ** 3 / X1 ** 2)
    ax=[ (2/3*C1*(C1*(x-rs[0])  )**(1/3) )*Mode.preset["m"] for x in rs]
    print(f'X1:{X1}')
    print(f'C1:{C1}')
    print(f'rs:{rs}')
    print(f'ax:{ax}')
    bx2.plot(r_mm,ax) #f=ma  ，计算力的第二种方法,a=v*dv/dr,a=dv/dt'''


    Ualllen = len(yss)- 2 - Mode.preset["coil_number"]
    Uall = yss[ - 2 - Ualllen :  -2 ]

    for j in range(Ualllen):
        bx3.plot(r_mm,Uall[j])
    

    bx4.plot(r_mm,v, color='red')


    ############ 画动画


    #画炮管
    xxx=[r_mm[0],r_mm[-1]]
    yyy=[1000*Mode.preset['Inner_diameter']/2,1000*Mode.preset['Inner_diameter']/2]
    bx5.plot(xxx,yyy, 'k--')
    xxx=[r_mm[0],r_mm[-1]]
    yyy=[-1000*Mode.preset['Inner_diameter']/2,-1000*Mode.preset['Inner_diameter']/2]
    bx5.plot(xxx,yyy, 'k--')

    textmaxy=1000*coils[0].Outer_d/2 +6

    #设置y轴边界
    text_ylim=textmaxy+16
    bx5.set_ylim(-text_ylim, text_ylim)  
    

    bx5.text(r_mm[0]-4, textmaxy,  '升温：', fontsize=8,ha='right') 
    bx5.text(r_mm[0]-4, textmaxy-4,  '线圈长度：', fontsize=8,ha='right') 
    
    bx5.text(r_mm[0]-4, -textmaxy+4,   '线径：', fontsize=8,ha='right') 
    bx5.text(r_mm[0]-4, -textmaxy+0,   '匝数：', fontsize=8,ha='right') 
    bx5.text(r_mm[0]-4, -textmaxy-4, '外径：', fontsize=8,ha='right') 
    bx5.text(r_mm[0]-4, -textmaxy-8, '脉宽：', fontsize=8,ha='right') 
    bx5.text(r_mm[0]-4, -textmaxy-12, '关断偏移：', fontsize=8,ha='right') 



    ###画线圈
    for coili,wirei,delta_Ti in zip(coils,Coil_arguments['wires'],Mode.delta_T_list):  
        Coil_Odi = coili.Outer_d*1000
        Coil_lengthi = coili.length*1000
        coil_loci = coili.r*1000
        
        xxx=[coil_loci-0.5*Coil_lengthi ,coil_loci-0.5*Coil_lengthi ,coil_loci+0.5*Coil_lengthi ,coil_loci+0.5*Coil_lengthi  ,coil_loci-0.5*Coil_lengthi]
        yyy=[-0.5*Coil_Odi              ,0.5*Coil_Odi               ,0.5*Coil_Odi               ,-0.5*Coil_Odi               ,-0.5*Coil_Odi] 
        bx5.plot(xxx,yyy, 'k-', color='red')
        
        
        bx5.text(coil_loci, textmaxy, f'{delta_Ti:.1f}', fontsize=8,ha='center') 
        bx5.text(coil_loci, textmaxy-4, f'{Coil_lengthi:.1f}', fontsize=8,ha='center')


        bx5.text(coil_loci, 0, f'{coili.i}', fontsize=8,ha='center') #线圈编号

        bx5.text(coil_loci, -textmaxy+4, f'{wirei:.2f}', fontsize=8,ha='center') 
        bx5.text(coil_loci, -textmaxy, f'{int(coili.N)}', fontsize=8,ha='center') 
        bx5.text(coil_loci, -textmaxy-4, f'{Coil_Odi:.1f}', fontsize=8,ha='center') 
        bx5.text(coil_loci, -textmaxy-8, f'{coili.pwi*1000:.1f}', fontsize=8,ha='center') 


        关断偏移=round( (Coil_arguments['td'][coili.i] +Coil_arguments['pw'][coili.i]-Coil_arguments['coil_location'][coili.i] +0.5*Mode.preset['slug_length']   ) *1000   ,2)
        bx5.text(coil_loci, -textmaxy-12, f'{关断偏移}', fontsize=8,ha='center') #

    # 初始化文本对象，用于显示slug_text
    slug_text = bx5.text(0.25, textmaxy+4, '')  

    ### 显示电流
    ###yss_amim = [bx5.text(0.25, textmaxy-4, '', fontsize=8,ha='center') for x in cnumnp]  
      
    #
    # 动子运动动画
    slug_anim, = bx5.plot([], [], lw=2)  

    # BField动画
    BField_anim, = bx5.plot([], [], lw=2)  

    t__r=interp1d(t, r_mm*1e-3, kind='linear', bounds_error=False, fill_value="extrapolate")
    t__v=interp1d(t, v  , kind='linear', bounds_error=False, fill_value="extrapolate")
    t__yssi=[interp1d(t, yss[i]  , kind='linear', bounds_error=False, fill_value="extrapolate")  for i in cnumnp               ]
    r__t=interp1d(r_mm*1e-3,t , kind='linear', bounds_error=False, fill_value="extrapolate")  
    t__U=interp1d(t, U  , kind='linear', bounds_error=False, fill_value="extrapolate")


#######
    max_B_sluglist=[]
    BField_B_list=[]
    for slug_t in np.linspace(0, t[-1], 200, endpoint=True)  :

       
        slug_loc= t__r(slug_t) 
        slug_v=t__v(slug_t) 
        slug_U=t__U(slug_t) 

        #######画行波
        BField=[]
        coilsneed = [coili for coili in coils if t__yssi[coili.i](slug_t)>1 ]
        for coili in coilsneed:    
            bFieldi = []
            for slug_ri in r_mm:
                bFieldi.append(  coili.BFieldCoil(slug_ri*1e-3 -coili.r, t__yssi[coili.i](slug_t) ) if  abs(slug_ri*1e-3 -coili.r) < 2*Mode.preset["slug_length"] else 0 )
            BField.append(bFieldi)
            
        BField_sum = [5*sum(col)-1000*Mode.preset["slug_R"]/2 for col in zip(*BField)]     # 使用zip函数和列表推导式按“列”相加  

        BField_B = [sum(col) for col in zip(*BField)]     # 使用zip函数和列表推导式按“列”相加 (10,12,14,16)



        try:
            BField_range = BField_sum
            if(len(r_mm)==len(BField_range)):

                BField_B_max_index = BField_B.index(max(BField_B))
                max_B_slug= slug_loc + 0.5*Mode.preset["slug_length"] - rs[BField_B_max_index]
                max_B_sluglist.append(max_B_slug)

        except:
            pass     

    def positive_ratio(lst):
        positive_count = sum(1 for num in lst if num > 0)
        ratio = positive_count / len(lst) if lst else 0  # 避免除以0
        return ratio


    #print(f'max_B_sluglist {max_B_sluglist}')
    print(f'负反馈占比：{positive_ratio(max_B_sluglist)*100:.2f}%,{sum(max_B_sluglist)}')



#########
    # 动画更新函数，每帧调用一次  
    def update(frame):  
        slug_t= frame*1e-4
        ##print("111")
        
        try :
            global slug_r_glaobal
            if slug_r_glaobal !=-1000:
                slug_t = r__t(slug_r_glaobal)
        except:
            pass     

  
        slug_loc= t__r(slug_t) 
        slug_v=t__v(slug_t) 
        slug_U=t__U(slug_t) 

        #######画行波
        BField=[]
        coilsneed = [coili for coili in coils if t__yssi[coili.i](slug_t)>1 ]
        for coili in coilsneed:    
            bFieldi = []
            for slug_ri in r_mm:
                bFieldi.append(  coili.BFieldCoil(slug_ri*1e-3 -coili.r, t__yssi[coili.i](slug_t) ) if  abs(slug_ri*1e-3 -coili.r) < 2*Mode.preset["slug_length"] else 0 )
            BField.append(bFieldi)
            
            
        BField_sum = [5*sum(col)-1000*Mode.preset["slug_R"]/2 for col in zip(*BField)]     # 使用zip函数和列表推导式按“列”相加  

        BField_B = [sum(col) for col in zip(*BField)]     # 使用zip函数和列表推导式按“列”相加 (10,12,14,16)
 
        try:
            BField_range = BField_sum
            if(len(r_mm)==len(BField_range)):
                BField_anim.set_data(r_mm,BField_range) 

                BField_B_max_index = BField_B.index(max(BField_B))
                #print(BField_B_max_index)
                #print(rs[BField_B_max_index])

                max_B_slug= slug_loc + 0.5*Mode.preset["slug_length"] - rs[BField_B_max_index]
                
                slug_text.set_text(f'电压={slug_U:.2f} |速度= {slug_v:.2f}m/s |位置= {slug_loc*1e+3:.2f}mm |时间= {slug_t*1e+6:.0f}us ,外部磁场峰值：{max(BField_B):.2f}T ,弹丸右端与磁场峰值的距离：{ max_B_slug*1e+3:.2f}  ,(弹丸右端出现在峰值右侧的比例)负反馈占比：{positive_ratio(max_B_sluglist)*100:.2f}%  '  )  
        
        except:
            pass        


        '''for slug_locx in np.linspace(slug_loc, slug_loc - Mode.preset["slug_R"], 21, endpoint=True)  :
            coili.BFieldCoil(slug_ri*1e-3 -coili.r, t__yssi[coili.i](slug_t) ) if  abs(slug_ri*1e-3 -coili.r) < 2*Mode.preset["slug_length"] else 0 
            coili.BFieldCoil()
            coili.calculate_F(self,D):'''


        #print(slug_loc)
        #动子运动 
        if Mode.useSteel_ball ==1:
            theta = np.linspace(0, 2*np.pi, 100)  # 生成角度参数
            r = 1000*Mode.preset["slug_R"] /2
            xxx = r * np.cos(theta) + 1000*slug_loc
            yyy = r * np.sin(theta)
            slug_anim.set_data(xxx, yyy)  
        else:
            xxx=[1000*(slug_loc-0.5*Mode.preset["slug_length"]) ,1000*(slug_loc-0.5*Mode.preset["slug_length"])   ,1000*(slug_loc+0.5*Mode.preset["slug_length"]) ,1000*(slug_loc+0.5*Mode.preset["slug_length"]) ,1000*(slug_loc-0.5*Mode.preset["slug_length"])]
            yyy=[-1000*0.5*Mode.preset["slug_R"]                  ,1000*0.5*Mode.preset["slug_R"]                     ,1000*0.5*Mode.preset["slug_R"]                   ,-1000*0.5*Mode.preset["slug_R"]                  ,-1000*0.5*Mode.preset["slug_R"]] 
            slug_anim.set_data(xxx, yyy)  




        
        '''#对电流图做标记 
        for texti,coili,cnumi in zip(yss_amim,coil_loc,cnumnp):  
            texti.set_x(coili)
            texti.set_text(str(int(t__yssi[cnumi](slug_t)  ) )   )   #, va='bottom'  '''

          
        aa=[slug_anim,slug_text,BField_anim]
        return  aa #+ yss_amim

    # 创建动画  
    ani = FuncAnimation(fig, update, frames=np.arange(0, int(t[-1]*1e+4)+1  ),  
                         blit=True)  

        

    if   combovalue['showplt'] == '仅显示位移图'   :       #显示位移图
        plt.show()  

    if   combovalue['showplt'] == '显示位移子图'   :       #显示位移图，block=False
        plt.show(block=False)  

    elif combovalue['showplt'] == '同时显示位移与时间图及时序图' :   #显示时间图
    
        plt.show(block=False)
        fig2 = plt.figure()  # 创建一个新的figure对象  
        cx1 = fig2.add_subplot(511)
        cx1.set_title(f'时间图') 
        cx1.set_ylabel("线圈电流 / A")
         
        cx2 = fig2.add_subplot(512, sharex = cx1)
        cx2.set_ylabel("力 / N")

        cx3 = fig2.add_subplot(513, sharex = cx1)
        cx3.set_ylabel("电容电压 / V")

        cx4 = fig2.add_subplot(514, sharex = cx1) 
        cx4.set_ylabel("动子速度 / m/s")
        
        
        cx4.set_xlabel(f"时间 / us  ")
        t_us=np.array(t)*1e+6


        for i in range(Mode.preset["coil_number"]):
            cx1.plot(t_us,yss[i])
            
        coil_loc=np.array( [ round(coili.r*1000,2) for coili in coils] )

        coil_num = [i  for i in range(Mode.preset["coil_number"])]

        ###########
        cx2.plot(t_us[1:],((v[1:]-v[:-1])/ (t[1:]-t[:-1])   )*Mode.preset["m"]) #f=ma  ，计算力的第二种方法,a=v*dv/dr,a=dv/dt

        cx3.plot(t_us,U)
        cx4.plot(t_us,v, color='red')

        plt.show(block=False)  
        
        #####显示时序图

        Array_n, Array_H, Array_L,zlsx = 根据原有数据生成阵列(Mode.preset["阵列规模输入"])


        plt.rcParams['figure.figsize']=(20.1,19)
        fig3=plt.figure()



        Timing = Mode.Timing
        if zlsx=='[]' or zlsx=='空缺' :  #空缺为普通半桥拓扑
            bx1 = fig3.add_subplot(211)
            bx1.set_title(f'时序图：普通半桥模式') 
            for i in range(len(Timing[0])):
                xxx=[Timing[0][i],Timing[0][i],Timing[0][i]+Timing[1][i],Timing[0][i]+Timing[1][i]]
                yyy=[-2*i,-2*i+1,-2*i+1,-2*i]
                bx1.plot(xxx,yyy , 'k-',color='blue')
                bx1.text((xxx[1]+xxx[2])/2, yyy[1], f'{i}', color='blue', ha='center') #, va='bottom'

        else :   #半桥阵列拓扑
            bx1 = fig3.add_subplot(211)
            bx1.set_title(f'时序图：半桥阵列拓扑模式') 
            bx2 = fig3.add_subplot(212, sharex = bx1)
            zlsx=eval(zlsx)

            HpinNum = max([  zlsx[i][1]   for i in range(len(zlsx))   ] ) +1
            LpinNum = max([  zlsx[i][1]   for i in range(len(zlsx))   ] ) +1
            print(f'HpinNum{HpinNum}  LpinNum {LpinNum}')

            for i in range(HpinNum):
                bx1.text(r_mm[0]-4, -i*2, f'H{i}', fontsize=8,ha='right') 

            for i in range(LpinNum):
                bx2.text(r_mm[0]-4, -i*2, f'L{i}', fontsize=8,ha='right') 

            for i in range(len(zlsx)):
                xxx=[Timing[0][i],Timing[0][i],Timing[0][i]+Timing[1][i],Timing[0][i]+Timing[1][i]]
                yyy=[-zlsx[i][1]*2,-zlsx[i][1]*2+1,-zlsx[i][1]*2+1,-zlsx[i][1]*2]
                bx1.plot(xxx,yyy , 'k-',color='blue')
                #bx1.text((xxx[1]+xxx[2])/2, yyy[1], f'{i}:({zlsx[i][1]},{zlsx[i][2]})', color='blue', ha='center') #, va='bottom'
                bx1.text((xxx[1]+xxx[2])/2, yyy[1], f'{i}:({Timing[0][i]}，{Timing[1][i]})', color='blue', ha='center') #, va='bottom'


                xxx=[Timing[0][i],Timing[0][i],Timing[0][i]+Timing[1][i],Timing[0][i]+Timing[1][i]]
                yyy=[-zlsx[i][2]*2,-zlsx[i][2]*2+1,-zlsx[i][2]*2+1,-zlsx[i][2]*2]
                bx2.plot(xxx,yyy , 'k-',color='blue')
                #bx2.text((xxx[1]+xxx[2])/2, yyy[1], f'{i}:({zlsx[i][1]},{zlsx[i][2]})', color='blue', ha='center') #, va='bottom'
                bx2.text((xxx[1]+xxx[2])/2, yyy[1],  f'{i}:({Timing[0][i]}，{Timing[1][i]})', color='blue', ha='center') #, va='bottom'

 
        try:    
            #print(script_directory+'\\'+'out_zlsx.png')
            plt.savefig(script_directory+'\\'+'out_zlsx.png', transparent=True)
            print('时序文件out_zlsx.png 保存成功')
        except:
            print('时序文件out_zlsx.png 保存失败')


        
        plt.show()                







#--------------------------------gui--------------------------------
  
def getmodei():
    global modecsv
    global combo_selectfile
    global mode_次要参数

    modecsv=script_directory+'\\' +str(combo_selectfile.get())
    print("mode.csv路径: "+modecsv)


    modecsv_encoding = get_file_encoding(modecsv)
    print(f"文件编码: {modecsv_encoding}")


    Modefile=pd.read_csv(modecsv, index_col=False, encoding= modecsv_encoding)

    mode_次要参数 = eval(Modefile.loc[0,'次要参数'])

    modeidata = np.array(  Modefile.loc[:, '线圈数量'].values)  #

    # 初始化一个变量来跟踪当前非nan序列的长度  
    current_length = 0  
    input_lines=[]
    input_line_start=0
    # 遍历数据列表  
    for i in range(len(modeidata)):  
        if isinstance(modeidata[i], str) or not np.isnan(modeidata[i]):  # 检查当前值是否不是nan  
            current_length += 1  # 增加当前序列的长度  
        else:  
            if current_length > 0:  # 如果当前序列长度大于0，则打印或处理它  
                input_lines.append([input_line_start+3,input_line_start+3+current_length-1])
            current_length = 0  # 重置当前序列的长度  
            input_line_start =i
            

    #追加最后一组数据
    input_lines.append([input_line_start+3,input_line_start+3+current_length-1]) 
    
    input_lines_dict=[   str([Modefile.loc[line[0]-2,'唯一id'] ,line[0],line[1] ])            for line in input_lines]
    
    
    
    # 初始化数据  
    data = input_lines_dict#[
    ##print(data)
    return data 






if __name__ == '__main__':  
    import Ai_Optuna

    multiprocessing.set_start_method("spawn")

    multiprocessing.freeze_support()

    print(f"版本号为：{__version__}\n")

    def reset_combobox():
        global combo_modei
        data=getmodei()
        combo_modei['values'] = (data)
        combo_modei.set(data[0])  # 直接设置显示的值


    def startmymain(button_name):
        #try :
        #    subprocess.run(f"taskkill /F /IM wps.exe", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
        #except Exception as e:  
        #    print(f"An error occurred while trying to kill the process: {e}")  

        global combo_modei
        global combo_Mcaulate_select
        global combo_showplt

        Mcaulate_select=combo_Mcaulate_select.get()

        getmodei()
        modidata = eval(combo_modei.get())  #[int(num) for num in combo_modei.get().split()]

        if button_name == '时间控制':
            global combo_input_timing
            global combo_input_受力系数

            Mode=CoilMode(modidata,0,'',Mcaulate_select,'时间控制')  
            Mode.input_Timing =  eval(combo_input_timing.get().replace("{", "[").replace("}", "]"))   
            Mode.lin_preset["Secondary_parameters"]['受力系数'] = eval(combo_input_受力系数.get())


        else :
            Mode=CoilMode(modidata,0,'',Mcaulate_select,'位移控制')


        print(f'输入数据：\n {Mode.lin_preset}\n' )

        ###普通模式
        if  Mode.lin_preset['PARAM_BOUNDS']  ==[] :

            resultdata = coilgunApi(Mode.lin_preset,'非参数扫描模式','显示线圈')
            figure_show(resultdata[6] , resultdata[7] , resultdata[8] , resultdata[9], resultdata[10]  , combovalue={'showplt': combo_showplt.get()} )



        ###电压弹长扫描模式
        elif ( len(Mode.lin_preset['PARAM_BOUNDS'] ) ==1 and  Mode.lin_preset['PARAM_BOUNDS'][0][0]== "[-1, 'U0']"  and Mode.lin_preset['PARAM_BOUNDS'][0][2]== "tuple"      ) \
             or ( len(Mode.lin_preset['PARAM_BOUNDS'] ) ==1 and  Mode.lin_preset['PARAM_BOUNDS'][0][0]== "[-1, 'slug_length']"  and Mode.lin_preset['PARAM_BOUNDS'][0][2]== "tuple"      ) \
             or (  len(Mode.lin_preset['PARAM_BOUNDS'] ) ==2 and Mode.lin_preset['PARAM_BOUNDS'][0][0]== "[-1, 'U0']"   and  Mode.lin_preset['PARAM_BOUNDS'][1][0]== "[-1, 'slug_length']"  and Mode.lin_preset['PARAM_BOUNDS'][0][2]== "tuple"  and Mode.lin_preset['PARAM_BOUNDS'][1][2]== "tuple"    )  :
            
            
            if len(Mode.lin_preset['PARAM_BOUNDS'] ) ==1: #补充至完整
                if Mode.lin_preset['PARAM_BOUNDS'][0][0]== "[-1, 'U0']":
                    x= ("[-1, 'slug_length']", 'float', 'tuple', [ Mode.lin_preset["slug_length"]  ])
                    Mode.lin_preset['PARAM_BOUNDS'].append(x )
                else:
                    x= ("[-1, 'U0']", 'float', 'tuple', [ Mode.lin_preset["U0"]  ])
                    Mode.lin_preset['PARAM_BOUNDS'].insert( 0, x) 

            print(Mode.lin_preset['PARAM_BOUNDS'])        
            Timing =[]
            coils = []
            时序编号=0

            for slug_length_i in Mode.lin_preset['PARAM_BOUNDS'][1][3]  :
                for U0_i in  Mode.lin_preset['PARAM_BOUNDS'][0][3] :
                    nowlin_preset = copy.deepcopy( Mode.lin_preset )

                    nowlin_preset['slug_length'] = slug_length_i
                    nowlin_preset['U0'] = U0_i

                    resultdata = coilgunApi(nowlin_preset,'电压弹长扫描模式')
                    ###figure_show(resultdata[6] , resultdata[7] , resultdata[8] , resultdata[9], resultdata[10]  , combovalue={'showplt': '显示位移子图'} )


                    Timing.append(resultdata[5][0])
                    Timing.append(resultdata[5][1])
                    coils =resultdata[6]

                    #print(f'弹长: {nowlin_preset['slug_length'] } ,电压: {nowlin_preset['U0']}  ,{resultdata[3]:.1f}m/s ,{resultdata[0] :.1f} J ,电流:{resultdata[1] :.1f} A ,电压*电流=  { nowlin_preset['U0']*resultdata[1]   }') #, {resultdata[5]}
                    print(f'时序编号id:{时序编号} 弹长: {nowlin_preset['slug_length'] } ,电压: {nowlin_preset['U0']}  ,{resultdata[3]:.1f}m/s ,最大电流:{resultdata[1] :.1f} A, 时序：{ str(resultdata[5]).replace('[', '{').replace(']', '}')  }') #, {resultdata[5]}
                    时序编号 +=1
            Mode.save(coils,Timing)   #保存数据          
                    


 
            #[("[-1, 'U0']", 'float', 'tuple', [450, 500]), ("[-1, 'slug_length']", 'float', 'tuple', [12, 16, 18])]  

        else:
            print("请使用ai优化")
            pass
            ###scanning_main(Mode)


        ###

       

    def startmymain2(button_name):  #分立半桥模拟

        global combo_modei
        
        getmodei()
        modidata = eval(combo_modei.get())  #[int(num) for num in combo_modei.get().split()]
        V_lin_result =0

        Mode=CoilMode([modidata[0],modidata[1],modidata[2]])
        Mode.lin_preset_To_preset()    

        Coil_arguments =calculate_all(Mode)
        coils = [ Coil(i,Coil_arguments   ,Mode)  for i in range(Mode.preset["coil_number"])]
        print(len(coils))
        #Mode.lin_preset['C'] = Mode.lin_preset['C'] /len(coils)


        for use_i in range(len(coils)):

            Mode.lin_preset['v0'] = V_lin_result

            #Mode.lin_preset['C'] = 
            #Mode.lin_preset['U0'] = 

            Mode.lin_preset_To_preset()    
            newcoils = [coils[use_i] ]
            newcoils[0].i=0


            print(f'newcoils')
            yss,rs=Slug_move(Mode,newcoils  ,1).main()
            Mode.calculate_result(newcoils,yss,rs,Coil_arguments)



            V_lin_result = Mode.v_result
            print(f'级数：{use_i}  速度：{Mode.v_result:.1f}  最大电流：{Mode.coil_maxA:.1f}A ')
        

    def startmymain3(button_name):  #ai参数优化

        global combo_modei
        global combo_Mcaulate_select
        global combo_input_CONSTRAINT_MAX
        global combo_input_TOTAL_TRIALS
        global combo_input_参数优化进程数


        getmodei()
        modidata = eval(combo_modei.get())  #[int(num) for num in combo_modei.get().split()]
        Mode=CoilMode(modidata,0,'')
        ##Mode.Mcaulate_select=combo_Mcaulate_select.get()

        CONSTRAINT_MAX= float(combo_input_CONSTRAINT_MAX.get())
        TOTAL_TRIALS= int(combo_input_TOTAL_TRIALS.get())
        NUM_PROCESSES = int(combo_input_参数优化进程数.get())
        
        Ai_Optuna.ai_Optuna(Mode.lin_preset,CONSTRAINT_MAX,TOTAL_TRIALS,NUM_PROCESSES)
    


    try :
        root = tk.Tk()
        entries = os.listdir(script_directory)
        files = [entry for entry in entries if entry.endswith('.csv')]

        global combo_selectfile
        combo_selectfile = ttk.Combobox(root, width=20 )
        combo_selectfile['values'] = (files)
        combo_selectfile.current(0)
        combo_selectfile.grid(row=4, column=0)

        data=getmodei()
        global combo_modei
        global mode_次要参数



        # 开始模拟按钮
        start_button = tk.Button(root, text='开始模拟（位移控制）', command=partial(startmymain, "位移控制"))
        start_button.grid(row=0, column=0)
        reset_button = tk.Button(root, text='刷新选项', command=reset_combobox)
        reset_button.grid(row=1, column=0)
        


        
        global combo_showplt
        combo_showplt = ttk.Combobox(root, width=20 )
        combo_showplt['values'] = (['仅显示位移图','同时显示位移与时间图及时序图'])
        combo_showplt.current(0)
        combo_showplt.grid(row=2, column=0)

        
        global combo_Mcaulate_select
        combo_Mcaulate_select = ttk.Combobox(root, width=20 )
        combo_Mcaulate_select['values'] = (['精细计算互感','粗略计算互感','不计算互感'])
        combo_Mcaulate_select.current(0)
        combo_Mcaulate_select.grid(row=3, column=0)
        



        

        # 初始选项框
        combo_modei = ttk.Combobox(root, width=20 )
        combo_modei['values'] = (data)
        combo_modei.current(0)
        combo_modei.grid(row=5, column=0)
         
        ####
        start_button_参数优化 = tk.Button(root, text='ai参数优化（位移控制）', command=partial(startmymain3, ""))
        start_button_参数优化.grid(row=0, column=1)

        tk.Label(root, text="输入最大电流约束").grid(row=1, column=1)
        global combo_input_CONSTRAINT_MAX
        combo_input_CONSTRAINT_MAX = ttk.Combobox(root, width=20 )
        combo_input_CONSTRAINT_MAX['values'] = (['340'])
        combo_input_CONSTRAINT_MAX.current(0)
        combo_input_CONSTRAINT_MAX.grid(row=2, column=1)

        tk.Label(root, text="输入优化调用次数").grid(row=3, column=1)
        global combo_input_TOTAL_TRIALS
        combo_input_TOTAL_TRIALS = ttk.Combobox(root, width=20 )
        combo_input_TOTAL_TRIALS['values'] = (['1000'])
        combo_input_TOTAL_TRIALS.current(0)
        combo_input_TOTAL_TRIALS.grid(row=4, column=1)

        tk.Label(root, text="参数优化进程数").grid(row=5, column=1)
        global combo_input_参数优化进程数
        combo_input_参数优化进程数 = ttk.Combobox(root, width=20 )
        combo_input_参数优化进程数['values'] = (['12'])
        combo_input_参数优化进程数.current(0)
        combo_input_参数优化进程数.grid(row=6, column=1)


        #######
        start_button_time = tk.Button(root, text='开始模拟（时间控制）', command=partial(startmymain, "时间控制"))
        start_button_time.grid(row=0, column=2)

        global combo_input_timing
        combo_input_timing = ttk.Combobox(root, width=20 )
        combo_input_timing['values'] = (['在此处输入时序'])
        combo_input_timing.current(0)
        combo_input_timing.grid(row=1, column=2)


        tk.Label(root, text="输入时间控制所用的受力系数：").grid(row=2, column=2)

        global combo_input_受力系数
        combo_input_受力系数 = ttk.Combobox(root, width=20 )
        combo_input_受力系数['values'] = (['输入时间控制所用的受力系数'])
        combo_input_受力系数.current(0)
        combo_input_受力系数.grid(row=3, column=2)

        tk.Label(root, text="输入时间控制所用的初始位置：").grid(row=4, column=2)
        global combo_input_初始位置
        combo_input_初始位置 = ttk.Combobox(root, width=20 )
        combo_input_初始位置['values'] = (['输入时间控制所用的初始位置'])
        combo_input_初始位置.current(0)
        combo_input_初始位置.grid(row=5, column=2)


       ##

        tk.Label(root, text="时序文件out_time.h 保存地址：").grid(row=0, column=3)
        global combo_input_文件地址
        combo_input_文件地址 = ttk.Combobox(root, width=20 )
        combo_input_文件地址['values'] = (['文件保存地址'])
        combo_input_文件地址.current(0)
        combo_input_文件地址.grid(row=1, column=3)

        tk.Label(root, text="弹丸长度范围：").grid(row=2, column=3)
        global combo_input_弹丸长度范围
        combo_input_弹丸长度范围 = ttk.Combobox(root, width=20 )
        combo_input_弹丸长度范围['values'] = (['弹丸长度范围'])
        combo_input_弹丸长度范围.config(state="readonly") 
        combo_input_弹丸长度范围.current(0)
        combo_input_弹丸长度范围.grid(row=3, column=3)



        tk.Label(root, text="电压范围：").grid(row=4, column=3)
        global combo_input_电压范围
        combo_input_电压范围 = ttk.Combobox(root, width=20 )
        combo_input_电压范围['values'] = (['电压范围'])
        combo_input_电压范围.config(state="readonly") 
        combo_input_电压范围.current(0)
        combo_input_电压范围.grid(row=5, column=3)

        tk.Label(root, text="时序文件out_time.h 内容：").grid(row=0, column=4)
        global combo_input_时序文件
        combo_input_时序文件 = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=50, height=10)
        combo_input_时序文件.grid(row=1, column=4, rowspan=10, sticky="ns")



        # 开始模拟按钮3
        #start_button2 = tk.Button(root, text='分立半桥开始模拟', command=partial(startmymain2, "分立半桥开始模拟"))
        #start_button2.grid(row=0, column=3)

        try:    
            combo_showplt.set( f'{ mode_次要参数['默认显示图表'] }'   )  # 直接设置显示的值
            combo_Mcaulate_select.set( f'{ mode_次要参数['默认互感计算'] }'   )  # 直接设置显示的值

            combo_input_CONSTRAINT_MAX.set( f'{ mode_次要参数['默认最大电流约束'] }'   )  # 直接设置显示的值
            combo_input_TOTAL_TRIALS.set( f'{ mode_次要参数['默认优化调用次数'] }'   )  # 直接设置显示的值

            combo_input_参数优化进程数.set( f'{ int(mode_次要参数['参数优化进程数']) }'   )  # 直接设置显示的值
          

        except:
            pass





        global slug_r_glaobal
        slug_r_glaobal=-1000
        root.mainloop()

    except Exception as e:
        print(f"\n 1454:{e}\n")  
